package org.xebia.roo.addon.openshift;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.List;
import java.util.logging.Logger;

import javax.xml.parsers.ParserConfigurationException;

import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.Service;
import org.osgi.service.component.ComponentContext;
import org.springframework.roo.metadata.MetadataService;
import org.springframework.roo.process.manager.FileManager;
import org.springframework.roo.project.ProjectOperations;
import org.springframework.roo.project.maven.Pom;
import org.springframework.roo.shell.CliCommand;
import org.springframework.roo.shell.CliOption;
import org.springframework.roo.shell.CommandMarker;
import org.springframework.roo.shell.Shell;
import org.springframework.roo.shell.converters.StaticFieldConverter;
import org.springframework.roo.support.util.Assert;
import org.springframework.roo.support.util.DomUtils;
import org.springframework.roo.support.util.FileUtils;
import org.springframework.roo.support.util.StringUtils;
import org.springframework.roo.support.util.XmlUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.SAXException;

import com.openshift.express.client.IApplication;
import com.openshift.express.client.ICartridge;
import com.openshift.express.client.IEmbeddableCartridge;
import com.openshift.express.client.IOpenShiftService;
import com.openshift.express.client.ISSHPublicKey;
import com.openshift.express.client.InvalidCredentialsOpenShiftException;
import com.openshift.express.client.OpenShiftEndpointException;
import com.openshift.express.client.OpenShiftException;
import com.openshift.express.client.SSHKeyPair;
import com.openshift.express.internal.client.ApplicationInfo;
import com.openshift.express.internal.client.InternalUser;
import com.openshift.express.internal.client.UserInfo;

@Component
@Service
public class OpenshiftCommands implements CommandMarker {

	private Logger log = Logger.getLogger(getClass().getName());

	@Reference
	private StaticFieldConverter staticFieldConverter;

	@Reference
	Shell shell;

	@Reference
	ProjectOperations projectOperations;

	@Reference
	MetadataService metadataService;

	@Reference
	FileManager fileManager;

	private static final int MAX_WAIT = 500;

	protected void activate(ComponentContext context) {
		staticFieldConverter.add(EmbeddableCartridge.class);
	}

	protected void deactivate(ComponentContext context) {
		staticFieldConverter.remove(EmbeddableCartridge.class);
	}

	// @CliCommand(value = "rhc create domain", help =
	// "Bind a registered rhcloud user to a domain in rhcloud.")
	public void createDomain(
			@CliOption(key = "namespace", mandatory = true, help = "Namespace for your application(s) (alphanumeric - max 16 chars)") String namespace,
			@CliOption(key = "rhLogin", mandatory = true, help = "Red Hat login (RHN or OpenShift login with OpenShift Express access) (required)") String rhLogin,
			@CliOption(key = "password", mandatory = true, help = "RHLogin password (required)") String password,
			@CliOption(key = "passPhrase", mandatory = true) String passPhrase,
			@CliOption(key = "privateKeyPath", mandatory = true) String privateKeyPath,
			@CliOption(key = "publicKeyPath", mandatory = true) String publicKeyPath)
			throws Exception {

		String baseUrl = Util.getDefaultBaseUrl();

		IOpenShiftService openshiftService = OpenShiftServiceFactory
				.create(baseUrl);
		try {
			ISSHPublicKey sshKey = SSHKeyPair.create(passPhrase,
					privateKeyPath, publicKeyPath);
			openshiftService.createDomain(namespace, sshKey, new InternalUser(
					rhLogin, password, openshiftService));
		} catch (InvalidCredentialsOpenShiftException e) {
			log.info("\nInvalid user credentials.  Please check your Red Hat login and password and try again.\n");
			log.info("Exception Message " + e.getMessage());
		} catch (OpenShiftException openShiftException) {
			log.info("Exception encountered " + openShiftException.getMessage());
		}
	}

	@CliCommand(value = "rhc create jbossas7 app", help = "Creates a new JBoss AS7 OpenShift Express Application ")
	public void createApplication(
			@CliOption(key = "applicationName", mandatory = true) String applicationName,
			@CliOption(key = "rhLogin", mandatory = true, help = "Red Hat login (RHN or OpenShift login with OpenShift Express access) (required)") String rhLogin,
			@CliOption(key = "password", mandatory = true, help = "RHLogin password (required)") String password)
			throws Exception {
		createANewApplication(applicationName, rhLogin, password,
				ICartridge.JBOSSAS_7);
	}

	@CliCommand(value = "rhc create jenkins app", help = "Creates a new Jenkins OpenShift Express Application ")
	public void createJenkinsApplication(
			@CliOption(key = "applicationName", mandatory = true, unspecifiedDefaultValue = "jenkins") String applicationName,
			@CliOption(key = "rhLogin", mandatory = true, help = "Red Hat login (RHN or OpenShift login with OpenShift Express access) (required)") String rhLogin,
			@CliOption(key = "password", mandatory = true, help = "RHLogin password (required)") String password)
			throws Exception {
		createANewApplication(applicationName, rhLogin, password,
				ICartridge.JENKINS_14);
	}

	@CliCommand(value = "rhc deploy", help = "Deploys code to OpenShift Express")
	public void deploy(
			@CliOption(key = "message", mandatory = false, help = "The commit message.") String message) {
		if (StringUtils.isBlank(message)) {
			message = "deploy";
		}
		shell.executeCommand("! git add .");
		shell.executeCommand("! git commit -m \"" + message + "\"");
		shell.executeCommand("! git push");
	}

	@CliCommand(value = "rhc pom", help = "Adds OpenShift Profile to pom.xml")
	public void addOpenShiftProfile() throws SAXException, IOException,
			ParserConfigurationException {

		final Pom pom = projectOperations.getFocusedModule();
		Assert.notNull(pom,
				"The pom is not available, so profile addition cannot be performed");

		log.info("Pom " + pom.getPath());

		InputStream inputStream = fileManager.getInputStream(pom.getPath());
		final Document document = XmlUtils.readXml(inputStream);
		final Element profilesElement = DomUtils.createChildIfNotExists(
				"profiles", document.getDocumentElement(), document);
		final Element profileElement = DomUtils.createChildIfNotExists(
				"profile", profilesElement, document);
		final Element idElement = DomUtils.createChildElement("id",
				profileElement, document);
		idElement.setTextContent("openshift");
		final Element buildElement = DomUtils.createChildIfNotExists("build",
				profileElement, document);

		final Element finalNameElement = DomUtils.createChildIfNotExists(
				"finalName", buildElement, document);
		finalNameElement.setTextContent("applicationName");
		final Element pluginsElement = DomUtils.createChildIfNotExists(
				"plugins", buildElement, document);

		final Element pluginElement = DomUtils.createChildIfNotExists("plugin",
				pluginsElement, document);
		final Element artifactIdElement = DomUtils.createChildIfNotExists(
				"artifactId", pluginElement, document);
		artifactIdElement.setTextContent("maven-war-plugin");

		final Element versionElement = DomUtils.createChildIfNotExists(
				"version", pluginElement, document);
		versionElement.setTextContent("2.1.1");
		final Element configurationElement = DomUtils.createChildIfNotExists(
				"configuration", pluginElement, document);
		final Element outputDirectoryElement = DomUtils.createChildIfNotExists(
				"outputDirectory", configurationElement, document);
		outputDirectoryElement.setTextContent("deployments");
		final Element warNameElement = DomUtils.createChildIfNotExists(
				"warName", configurationElement, document);
		warNameElement.setTextContent("ROOT");

		fileManager.createOrUpdateTextFileIfRequired(pom.getPath(),
				XmlUtils.nodeToString(document), "added openshift profile",
				false);

	}

	@CliCommand(value = "rhc user info", help = "Displays information about user's OpenShift Express applications")
	public void userInfo(
			@CliOption(key = "rhLogin", mandatory = true, help = "Red Hat login (RHN or OpenShift login with OpenShift Express access) (required)") String rhLogin,
			@CliOption(key = "password", mandatory = true, help = "RHLogin password (required)") String password)
			throws Exception {
		String baseUrl = Util.getDefaultBaseUrl();

		IOpenShiftService openshiftService = OpenShiftServiceFactory
				.create(baseUrl);
		try {
			UserInfo info = openshiftService.getUserInfo(new InternalUser(
					rhLogin, password, openshiftService));
			log.info("\nApplications on OpenShift Express:\n");
			for (ApplicationInfo app : info.getApplicationInfos()) {
				log.info(Util.formatApplicationInfo(app, info.getNamespace(),
						info.getRhcDomain()));
			}
		} catch (InvalidCredentialsOpenShiftException e) {
			log.info("\nInvalid user credentials.  Please check your Red Hat login and password and try again.\n");
			log.info("Exception Message " + e.getMessage());
		}catch (OpenShiftException e) {
			log.info("OpenShift failed to get user information");
			log.info(e.getMessage());
			if (e.getCause().getClass() != null)
				log.info(e.getCause().getMessage());
		}
	}

	@CliCommand(value = "rhc cartridge list", help = "Lists all the avaliable embeddable cartridges")
	public void listEmbeddableCartridges(
			@CliOption(key = "rhLogin", mandatory = true, help = "Red Hat login (RHN or OpenShift login with OpenShift Express access) (required)") String rhLogin,
			@CliOption(key = "password", mandatory = true, help = "RHLogin password (required)") String password)
			throws Exception {
		String baseUrl = Util.getDefaultBaseUrl();

		IOpenShiftService openshiftService = OpenShiftServiceFactory
				.create(baseUrl);
		try {
			List<IEmbeddableCartridge> embeddableCartridges = openshiftService
					.getEmbeddableCartridges(new InternalUser(rhLogin,
							password, openshiftService));
			log.info("List of supported embedded cartridges:\n");
			log.info("Obtaining list of cartridges (please excuse the delay)...");
			for (IEmbeddableCartridge cartridge : embeddableCartridges) {
				log.info(cartridge.getName());
			}
		} catch (InvalidCredentialsOpenShiftException e) {
			log.info("\nInvalid user credentials.  Please check your Red Hat login and password and try again.\n");
			log.info("Exception Message " + e.getMessage());
		}catch (OpenShiftException e) {
			log.info("OpenShift failed to list all the avaliable embeddable cartridges");
			log.info(e.getMessage());
			if (e.getCause().getClass() != null)
				log.info(e.getCause().getMessage());
		}
	}

	@CliCommand(value = "rhc cartridge add", help = "Adds a cartridge to the application")
	public void embedCartridges(
			@CliOption(key = "applicationName", mandatory = true) String applicationName,
			@CliOption(key = "rhLogin", mandatory = true, help = "Red Hat login (RHN or OpenShift login with OpenShift Express access) (required)") String rhLogin,
			@CliOption(key = "password", mandatory = true, help = "RHLogin password (required)") String password,
			@CliOption(key = "cartridge", mandatory = true, help = "Name of the catridge to embed.") EmbeddableCartridge cartridge)
			throws Exception {
		String baseUrl = Util.getDefaultBaseUrl();

		IOpenShiftService openshiftService = OpenShiftServiceFactory
				.create(baseUrl);
		IEmbeddableCartridge embeddableCartridge = CartridgeFactory
				.getCartridge(cartridge);
		try {
			IEmbeddableCartridge embeddedCartridge = openshiftService
					.addEmbeddedCartridge(applicationName, embeddableCartridge,
							new InternalUser(rhLogin, password,
									openshiftService));
			log.info("Embedded cartridge: " + embeddedCartridge.getName());
			log.info(embeddedCartridge.getCreationLog());
		} catch (InvalidCredentialsOpenShiftException e) {
			log.info("\nInvalid user credentials.  Please check your Red Hat login and password and try again.\n");
			log.info("Exception Message " + e.getMessage());
		}catch (OpenShiftException e) {
			log.info("OpenShift failed to embed cartridge in the application");
			log.info(e.getMessage());
			if (e.getCause().getClass() != null)
				log.info(e.getCause().getMessage());
		}

	}

	@CliCommand(value = "rhc app start", help = "Starts the deployed application")
	public void startApplication(
			@CliOption(key = "applicationName", mandatory = true) String applicationName,
			@CliOption(key = "rhLogin", mandatory = true, help = "Red Hat login (RHN or OpenShift login with OpenShift Express access) (required)") String rhLogin,
			@CliOption(key = "password", mandatory = true, help = "RHLogin password (required)") String password)
			throws Exception {

		String baseUrl = Util.getDefaultBaseUrl();

		IOpenShiftService openshiftService = OpenShiftServiceFactory
				.create(baseUrl);
		try {
			IApplication application = openshiftService.startApplication(
					applicationName, ICartridge.JBOSSAS_7, new InternalUser(
							rhLogin, password, openshiftService));
			log.info("Started application " + application.getName() + " at "
					+ application.getApplicationUrl());
		} catch (InvalidCredentialsOpenShiftException e) {
			log.info("\nInvalid user credentials.  Please check your Red Hat login and password and try again.\n");
			log.info("Exception Message " + e.getMessage());
		}catch (OpenShiftException e) {
			log.info("OpenShift failed to start the application");
			log.info(e.getMessage());
			if (e.getCause().getClass() != null)
				log.info(e.getCause().getMessage());
		}
	}

	@CliCommand(value = "rhc app stop", help = "stops the deployed application")
	public void stopApplication(
			@CliOption(key = "applicationName", mandatory = true) String applicationName,
			@CliOption(key = "rhLogin", mandatory = true, help = "Red Hat login (RHN or OpenShift login with OpenShift Express access) (required)") String rhLogin,
			@CliOption(key = "password", mandatory = true, help = "RHLogin password (required)") String password)
			throws Exception {

		String baseUrl = Util.getDefaultBaseUrl();

		IOpenShiftService openshiftService = OpenShiftServiceFactory
				.create(baseUrl);
		try {
			IApplication application = openshiftService.stopApplication(
					applicationName, ICartridge.JBOSSAS_7, new InternalUser(
							rhLogin, password, openshiftService));
			log.info("Stopped application " + application.getName() + " at "
					+ application.getApplicationUrl());
		} catch (InvalidCredentialsOpenShiftException e) {
			log.info("\nInvalid user credentials.  Please check your Red Hat login and password and try again.\n");
			log.info("Exception Message " + e.getMessage());
		}catch (OpenShiftException e) {
			log.info("OpenShift failed to stop the application");
			log.info(e.getMessage());
			if (e.getCause().getClass() != null)
				log.info(e.getCause().getMessage());
		}
	}

	@CliCommand(value = "rhc app restart", help = "Restarts the deployed application")
	public void restartApplication(
			@CliOption(key = "applicationName", mandatory = true) String applicationName,
			@CliOption(key = "rhLogin", mandatory = true, help = "Red Hat login (RHN or OpenShift login with OpenShift Express access) (required)") String rhLogin,
			@CliOption(key = "password", mandatory = true, help = "RHLogin password (required)") String password)
			throws Exception {

		String baseUrl = Util.getDefaultBaseUrl();

		IOpenShiftService openshiftService = OpenShiftServiceFactory
				.create(baseUrl);
		try {
			IApplication application = openshiftService.restartApplication(
					applicationName, ICartridge.JBOSSAS_7, new InternalUser(
							rhLogin, password, openshiftService));
			log.info("Restarted application " + application.getName() + " at "
					+ application.getApplicationUrl());
		} catch (InvalidCredentialsOpenShiftException e) {
			log.info("\nInvalid user credentials.  Please check your Red Hat login and password and try again.\n");
			log.info("Exception Message " + e.getMessage());
		}catch (OpenShiftException e) {
			log.info("OpenShift failed to restart the application");
			log.info(e.getMessage());
			if (e.getCause().getClass() != null)
				log.info(e.getCause().getMessage());
		}
	}

	@CliCommand(value = "rhc app status", help = "Gives status of the deployed application")
	public void statusApplication(
			@CliOption(key = "applicationName", mandatory = true) String applicationName,
			@CliOption(key = "rhLogin", mandatory = true, help = "Red Hat login (RHN or OpenShift login with OpenShift Express access) (required)") String rhLogin,
			@CliOption(key = "password", mandatory = true, help = "RHLogin password (required)") String password)
			throws Exception {

		String baseUrl = Util.getDefaultBaseUrl();

		IOpenShiftService openshiftService = OpenShiftServiceFactory
				.create(baseUrl);
		try {
			String status = openshiftService.getStatus(applicationName,
					ICartridge.JBOSSAS_7, new InternalUser(rhLogin, password,
							openshiftService));
			log.info("Status ... " + status);
		} catch (InvalidCredentialsOpenShiftException e) {
			log.info("\nInvalid user credentials.  Please check your Red Hat login and password and try again.\n");
			log.info("Exception Message " + e.getMessage());
		}
	}

	@CliCommand(value = "rhc app destroy", help = "Destroys the application")
	public void destroyApplication(
			@CliOption(key = "applicationName", mandatory = true) String applicationName,
			@CliOption(key = "rhLogin", mandatory = true, help = "Red Hat login (RHN or OpenShift login with OpenShift Express access) (required)") String rhLogin,
			@CliOption(key = "password", mandatory = true, help = "RHLogin password (required)") String password)
			throws Exception {

		String baseUrl = Util.getDefaultBaseUrl();

		IOpenShiftService openshiftService = OpenShiftServiceFactory
				.create(baseUrl);
		try {
			openshiftService.destroyApplication(applicationName,
					ICartridge.JBOSSAS_7, new InternalUser(rhLogin, password,
							openshiftService));
			log.info("Destroyed application " + applicationName
					+ " on OpenShift Express");
		} catch (InvalidCredentialsOpenShiftException e) {
			log.info("\nInvalid user credentials.  Please check your Red Hat login and password and try again.\n");
			log.info("Exception Message " + e.getMessage());
		}catch (OpenShiftException e) {
			log.info("OpenShift failed to destroy the application");
			log.info(e.getMessage());
			if (e.getCause().getClass() != null)
				log.info(e.getCause().getMessage());
		}
	}

	private void createANewApplication(String applicationName, String rhLogin,
			String password, ICartridge applicationType) throws Exception {

		String baseUrl = Util.getDefaultBaseUrl();

		IOpenShiftService openshift = OpenShiftServiceFactory.create(baseUrl);
		IApplication application = null;
		try {
			application = openshift.createApplication(applicationName,
					applicationType, new InternalUser(rhLogin, password,
							openshift));
		} catch (InvalidCredentialsOpenShiftException e) {
			log.info("\nInvalid user credentials.  Please check your Red Hat login and password and try again.\n");
			return;
		} catch (OpenShiftEndpointException e) {
			log.info("OpenShift failed to create the application");
			log.info(e.getMessage());
			if (e.getCause().getClass() != null)
				log.info(e.getCause().getMessage());
			return;
		}

		log.info("Waiting for OpenShift to propagate DNS");
		if (!waitForExpress(application.getApplicationUrl())) {
			log.info("OpenShift did not propagate DNS properly");
			return;
		}

		cloneGitRepository(applicationName, application);
		String rootPath = ".";
		String applicationPath = rootPath
				+ File.separator + applicationName;
		File checkedOutApplication = new File(applicationPath);
		log.info("Application Path " + applicationPath);
		if (!checkedOutApplication.isDirectory()) {
			log.info("There was a problem in checking out the code from "
					+ application.getGitUri());
			log.info("Cleaning up. Please wait...");
			destroyApplication(applicationName, rhLogin, password);
			log.info("Cleaning Done. Please recreate the application.");
			return;
		}

		log.info("Cloned the git repository");

		log.info("Application created and deployed to "
				+ application.getApplicationUrl());

	}

	private void cloneGitRepository(String applicationName,
			IApplication application) throws OpenShiftException {

		StringBuilder gitCloneCommandBuilder = new StringBuilder();
		gitCloneCommandBuilder.append("git clone").append(" ")
				.append(application.getGitUri()).append(" ")
				.append(applicationName);

		String gitCloneCommand = gitCloneCommandBuilder.toString();
		log.info("Git Clone Command : " + gitCloneCommand);

		shell.executeCommand("! " + gitCloneCommand);
	}

	private boolean waitForExpress(String url) {
		for (int i = 0; i < MAX_WAIT; i++) {
			try {
				if (i % 5 == 0)
					log.info("Trying to contact " + url + " (attempt "
							+ (i + 1) + " of " + MAX_WAIT + ")");
				HttpURLConnection connection = (HttpURLConnection) new URL(url)
						.openConnection();
				if (connection.getResponseCode() == HttpURLConnection.HTTP_OK)
					return true;
			} catch (Exception e) {
				log.info("Encountered exception " + e.getMessage());
				log.info("Will retry again..");
			}
			try {
				Thread.sleep(500);
			} catch (InterruptedException e) {
				Thread.currentThread().interrupt();
			}
		}
		return false;
	}
	
	public static void main(String[] args) {
		String applicationPath = FileUtils.CURRENT_DIRECTORY
				+ File.separator + "aaa";
		System.out.println(applicationPath);
	}

}